﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PJ.Business.Partner;
using PJ.Framework.Common;
using PJ.Data.SalePurchase;
using PJ.Business.Inventory;
using PJ.Data;

namespace PJ.Business.SalePurchase
{
    /// <summary>
    /// ShoppingCartItemService class
    /// </summary>
    /// <typeparam name="O">Object to serve type</typeparam>
    /// <typeparam name="OD">Inner data object type</typeparam>
    /// <typeparam name="ODP">Inner data object provider type</typeparam>
    public abstract class ShoppingCartItemService<OI, OD, ODP> : DataObjectService<OI, OD, ODP>, IShoppingCartItemService<OI>
        where OI : class, IShoppingCartItem
        where OD : class, IShoppingCartItemData
        where ODP : class, IShoppingCartItemProvider<OD>
    {
        #region Constructor

        public ShoppingCartItemService(ODP dataService)
            : base(dataService)
        {
        }
        #endregion
        #region Interface implementation

        /// <summary>
        /// Inventory item service
        /// </summary>
        public IInventoryItemService InventoryItemService
        {
            get
            {
                if (_InventoryItemService == null)
                    _InventoryItemService = InjectionBroker.Resolve<IInventoryItemService>();
                return _InventoryItemService;
            }
        }

        #endregion

        #region Interface implementation

        public abstract IShoppingCartService ShoppingCartService { get; }

        public abstract IInventoryItemOrderParamService InventoryItemOrderParamService { get; }

        public string[] GetFragments(string parentId, PJ.Data.GetDataMode mode)
        {
            return DataService.GetFragments(parentId, mode);
        }

        public double Up(string parentId, double order)
        {
            return DataService.Up(parentId, order);
        }

        public double Down(string parentId, double order)
        {
            return DataService.Down(parentId, order);
        }

        public bool IsFirst(string parentId, double order)
        {
            return DataService.IsFirst(parentId, order);
        }

        public bool IsLast(string parentId, double order)
        {
            return DataService.IsLast(parentId, order);
        }

        #endregion

        #region Interface override

        public void Up(IOrderedFragment item)
        {
            if (item == null)
                throw new ArgumentNullException("item");
            item.Order = Up(item.OwnerId, item.Order);
        }

        public void Down(IOrderedFragment item)
        {
            if (item == null)
                throw new ArgumentNullException("item");
            item.Order = Down(item.OwnerId, item.Order);
        }

        public bool IsFirst(IOrderedFragment item)
        {
            if (item == null)
                throw new ArgumentNullException("item");
            return IsFirst(item.OwnerId, item.Order);
        }

        public bool IsLast(IOrderedFragment item)
        {
            if (item == null)
                throw new ArgumentNullException("item");
            return IsLast(item.OwnerId, item.Order);
        }
        public IEntityService OwnerService
        {
            get { return ShoppingCartService; }
        }
        #endregion

        #region Public


        /// <summary>
        /// Find an item.
        /// </summary>
        /// <param name="identifier">the item's id, name alias or barcode can be used.</param>
        /// <param name="itemId">found item id</param>
        /// <param name="quantity">the barcode quantity if used</param>
        /// <param name="defaultQuantity">the default item order quantity</param>
        /// <param name="unitId">The default item order unit id or barcode unit id if used.</param>
        /// <returns>true if the item is found else false. Out parameters have default values if returns false</returns>
        public bool FindItemId(string identifier, out string itemId, out double quantity, out double defaultQuantity, out string unitId)
        {
            IInventoryItem item;
            if (identifier != null && identifier != "")
            {
                item = (IInventoryItem)InventoryItemService.GetById(identifier, GetDataMode.CachedFind);
                if (item == null)
                {
                    item = (IInventoryItem)InventoryItemService.GetByAlias(identifier, GetDataMode.CachedFind);
                    if (item == null)
                    {
                        IInventoryBarCode barcode = (IInventoryBarCode)InventoryItemService.InventoryBarCodeService.GetById(identifier, GetDataMode.CachedFind);
                        if (barcode != null)
                        {
                            item = (IInventoryItem)InventoryItemService.GetById(barcode.OwnerId, GetDataMode.UseCache);
                            itemId = barcode.OwnerId;
                            quantity = barcode.Quantity;
                            defaultQuantity = 0;
                            unitId = barcode.UnitId;
                            return true;
                        }
                    }
                }
            }
            else
                item = null;
            if (item == null)
            {
                itemId = null;
                quantity = 0;
                defaultQuantity = 0;
                unitId = null;
                return false;
            }
            else
            {
                itemId = item.Id;
                IInventoryItemOrderParam param = (IInventoryItemOrderParam)InventoryItemOrderParamService.GetById(itemId, GetDataMode.CachedFind);
                if (param == null)
                {
                    quantity = 0;
                    defaultQuantity = 0;
                    unitId = null;
                }
                else
                {
                    quantity = 0;
                    defaultQuantity = param.DefaultQuantity;
                    unitId = param.UnitId;
                }
                return true;
            }
        }
        /// <summary>
        /// Decodes a user expression string. 
        /// </summary>
        /// <param name="expression">the user expression to decode</param>
        /// <param name="itemId">Found Item id</param>
        /// <param name="quantity">Decoded Quantity</param>
        /// <param name="unitId">Quantity unit id. From barcode if used or default item unit.</param>
        /// <returns>true if decoded without errors and item found, else return false. Out parameters have default values if returns false.</returns>
        /// <remarks>The last '*' in the expression will seperate quantity expression from item expresssion.
        /// If the item expression is a barcode the returned quantity is the barcode's quantity multiplied by user expression quantity.
        /// If there are not any quantity defined in the expression, the default item quantity will be returned
        /// </remarks>
        /// <example>3*56+3*itemabc => 
        ///             Quantity expression = 3*56+3 = 171
        ///             Item expression = itemabc = IT25664 (itemabc is alias of IT25664)
        /// </example>
        public bool DecodeExpression(string expression, out string itemId, out double quantity, out string unitId)
        {
            if (expression != null && expression != "")
            {
                string itemExpression, qtyExpression;
                int sepPosition = expression.LastIndexOf('*'); /* quantity expression separator position */
                if (sepPosition == -1)
                {
                    itemExpression = expression;
                    qtyExpression = null;
                }
                else
                {
                    itemExpression = expression.Substring(sepPosition + 1);
                    if (sepPosition == 0)
                        qtyExpression = null;
                    else
                        qtyExpression = expression.Substring(0, sepPosition);
                }

                if (itemExpression != null && itemExpression != "")
                {
                    double expressionQty = 0;
                    if (qtyExpression != null && qtyExpression != "") // User has entered a quantity expression
                        PJ.Framework.ExpressionCalculator.Calc(qtyExpression, out expressionQty);
                    double defaultQuantity;
                    double tmpQty;
                    if (FindItemId(itemExpression, out itemId, out tmpQty, out defaultQuantity, out unitId))
                    {
                        if (tmpQty == 0) // Quantity not defined by barcode
                            if (expressionQty == 0)
                                quantity = (defaultQuantity == 0) ? 1 : defaultQuantity; // Use item default quantity
                            else
                                quantity = expressionQty; // Use user expression quantity
                        else 
                            if (expressionQty == 0)
                                quantity = tmpQty;
                            else
                                quantity = tmpQty * expressionQty; // Multiply barcode quantity by user expression quantity
                        return true;
                    }
                }
            }
            itemId = null;
            quantity = 0;
            unitId = null;
            return false;
        }



        #endregion

        #region Field

        private IInventoryItemService _InventoryItemService;

        #endregion

    }
}
